package com.vonchenchen.elasticscrollview.ui;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnticipateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.ScrollView;
import android.widget.Toast;

/**
 * 带上下拉的scrollview    默认只有上拉
 * Created by vonchenchen on 2015/11/11 0011.
 *
 * 思路：
 *      在以下两种情况下出现弹性效果
 *
 *      1.自然滚动超越屏幕
 *      onOverScrolled
 *          STATUS_SCROLLOVER
 *          if 手离开屏幕 && Y轴有滚动
 *              -只做滚出然后弹回的动画效果autoScrollAnimation
 *
 *          动画完毕恢复状态STATUS_NORMAL
 *
 *      2.拖动超越屏幕(或者外层容器)
 *      onTouchEvent
 *          if 滚动距离+scrollview外边框高度==内部控件的实际高度(即滚动到最底下时拖动) || y轴没有滚动(长度不够滚动时拖动)
 *              -上拉  STATUS_DRAG
 *                                  拉动+松手回调+弹回动画reductAnimation
 *              -下拉  STATUS_PULL
 *                                  拉动+松手回调+弹回动画reductAnimation
 *
 *           动画完毕恢复状态STATUS_NORMAL
 *
 * 细节问题
 *      1.关于控件移动的方法
 *          本例程控件移动使用控件的setTranslationY方法
 *
 *      2.判断由滚动切换到拖动需要的数据
 *
 *          获取滚动距离           getScrollY()
 *          scrollview外边框高度   getHeight()                           此处需要区别scrollview的高度是屏幕内可见的范围
 *          内部控件的实际高度     内部控件.getMeasuredHeight();         scrollview中真正长的其实是scrollview内部的控件
 */
public class ElasticScrollView extends ScrollView {

    private Context mCtx;

    private int mCurrentStatus = STATUS_NORMAL;
    private static final int STATUS_NORMAL = 0;
    /** 上拖 */
    private static final int STATUS_DRAG = 1;
    /** 下拉 */
    private static final int STATUS_PULL = 3;

    private static final int STATUS_RELEASE = 2;
    /** 滚动中碰到边界 */
    private static final int STATUS_SCROLLOVER = 4;

    private boolean mIsTouch = false;

    /** ElasticScrollView控件可见高度 */
    private int mViewHeight;
    /** Y轴滚动长度 */
    private int mYScrollLengh;
    /** Y轴拖动长度 */
    private int mYDrahLengh;
    /** ElasticScrollView 中内部控件真实高度 */
    private int mScrollViewMeasuedHeight;
    /** Y轴落点 */
    private int mDownY;
    /** Y轴移动点 */
    private int mMoveY = 0;
    private int mMoveYRec = 0;
    /** Y轴开始计算移动距离的点 */
    private int mStartMoveY = 0;
    /** pull或drag的实际距离 */
    private int mMoveLength = 0;

    /** ElasticScrollView中的唯一子控件 */
    private View mChildView;

    /** 是否允许下拉 默认不允许*/
    private boolean mPullDownEnable = true;
    /** 是否允许上拉 默认允许 */
    private boolean mDragUpEnable = true;

    /** Y轴滚动速度 */
    private int mVelocityY;

    private OnRefreshListener mOnRefreshListener;

    public ElasticScrollView(Context context) {
        super(context);
        this.mCtx = context;
    }

    public ElasticScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mCtx = context;
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {

    }

    @Override
    public void fling(int velocityY) {
        super.fling(velocityY);

        this.mVelocityY = velocityY;
    }

    /**
     * 自然滚动超越屏幕
     * @param scrollX
     * @param scrollY
     * @param clampedX
     * @param clampedY
     */
    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);

        Log.i("ElasticScrollView1", "scrollY " + scrollY + "  clampedY " + clampedY + "  " + mIsTouch);
        //1.手未接触，滚动到底
        if(!mIsTouch && clampedY){

            Log.i("ElasticScrollView1", "scroll over!!!");

            mCurrentStatus = STATUS_SCROLLOVER;
            //向下滚动距离
            int dataLength = Math.abs(mVelocityY/50);
            //向下滚动时间
            int dataTime = Math.abs(mVelocityY/50);

            if(scrollY < 0 && mPullDownEnable){            //向下滚动  滑到顶端
                autoScrollAnimation(mChildView, dataLength, dataTime);
            }else if(scrollY > 0 && mDragUpEnable){     //向上滚动  滑到底端
                autoScrollAnimation(mChildView, -dataLength, dataTime);
            }
        }
        mScrollViewMeasuedHeight = mChildView.getMeasuredHeight();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mChildView = this.getChildAt(0);

        setSmoothScrollingEnabled(false);
    }

    /**
     * 是否允许下拉
     * @param enable
     */
    public void setPullDownEnable(boolean enable){
        this.mPullDownEnable = enable;
    }

    public void setDragUpEnable(boolean enable){
        this.mDragUpEnable = enable;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        int action = ev.getAction();

        mYScrollLengh = this.getScrollY();
        switch (action){
            case MotionEvent.ACTION_DOWN:

                mIsTouch = true;                     //触摸标志位置位
                mDownY = (int) ev.getY();             //按下Y轴基准
                mViewHeight = this.getHeight();      //获取当前ScrollView的高度
                mScrollViewMeasuedHeight = mChildView.getMeasuredHeight();//获取内层控件的高度

                mCurrentStatus = STATUS_NORMAL;
                mYDrahLengh = 0;                     //Y轴拖动距离复位

                break;
            case MotionEvent.ACTION_MOVE:
                mMoveY = (int) ev.getY();

                mYDrahLengh = mDownY - mMoveY;   //获取Y轴真实拖动距离
                mYDrahLengh = mYDrahLengh/2;     //转换为比例拖动距离
                //上拉
                if(mYDrahLengh > 0 && mDragUpEnable) {
                    //两种情况，切换到拉动
                    //1.mYScrollLengh == 0                    scrollview中内容较短，没有滑动起来 时拉动
                    //2.mYScrollLengh + mViewHeight == mScrollViewMeasuedHeight       滚动，并且滚动到头 时拉动
                    //     当滑动距离+scrollView == scrollView内部控件高度  说明滚动到头，切换为拉动
                    if (mYScrollLengh + mViewHeight == mScrollViewMeasuedHeight || (mYScrollLengh == 0)) { //上拉
                        if(mCurrentStatus == STATUS_NORMAL){
                            mStartMoveY = mMoveY;
                        }
                        mCurrentStatus = STATUS_DRAG;
                        //根据拖到距离设置控件实际移动距离
                        mMoveLength = (mStartMoveY - mMoveY)/2;
                        //移动控件
                        mChildView.setTranslationY(-mMoveLength);
                    }
                }else if(mYDrahLengh < 0 && mPullDownEnable){     //下拉

                    //如果滚动距离为0  则切换为下拉状态
                    if(mYScrollLengh == 0) {
                        if(mCurrentStatus == STATUS_NORMAL){
                            mStartMoveY = mMoveY;
                        }
                        mCurrentStatus = STATUS_PULL;

                        mMoveLength = (mStartMoveY - mMoveY)/2;
                        mChildView.setTranslationY(-mMoveLength);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:

                if(mCurrentStatus == STATUS_DRAG){
                    reductAnimation(mChildView, mMoveLength);
                }else if(mCurrentStatus == STATUS_PULL){
                    reductAnimation(mChildView, mMoveLength);
                }

                mIsTouch = false;
                break;
        }
        return super.onTouchEvent(ev);
    }

    /**
     *  拖动恢复动画   弹回来
     * @param view
     * @param mData
     */
    private void reductAnimation(final View view, float mData){

        final float moveLength = Math.abs(mData);
        int tmp = (int) mData;
        mData = -mData;
        final ValueAnimator animator = ValueAnimator.ofFloat(mData, 0);
        animator.setDuration(Math.abs(tmp));
        //animator.setDuration(Math.abs(10000));
        animator.setInterpolator(new AccelerateInterpolator());//AccelerateInterpolator  LinearInterpolator
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float data = (float) animator.getAnimatedValue();
                    view.setTranslationY(data);
//                Log.i("ElasticScrollView1", "data "+data);
            }
        });

        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
            }
            @Override
            public void onAnimationEnd(Animator animator) {
                if(mOnRefreshListener != null){
                    if(mCurrentStatus == STATUS_DRAG) {
                        mOnRefreshListener.dragFinish(view, moveLength);
                    }else if(mCurrentStatus == STATUS_PULL){
                        mOnRefreshListener.pullFinish(view, moveLength);
                    }
                }
                mCurrentStatus = STATUS_NORMAL;
            }
            @Override
            public void onAnimationCancel(Animator animator) {
            }
            @Override
            public void onAnimationRepeat(Animator animator) {
            }
        });

        animator.start();
    }

    /**
     * 滚动过界动画效果   滚下去，弹回来
     * @param view
     * @param mData
     * @param duration
     */
    private void autoScrollAnimation(final View view, final float mData,final  int duration){
        final ValueAnimator animator = ValueAnimator.ofFloat(0, mData);
        animator.setDuration(duration);
        //animator.setDuration(Math.abs(10000));
        animator.setInterpolator(new DecelerateInterpolator());//AccelerateInterpolator  LinearInterpolator
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float data = (float) animator.getAnimatedValue();
                //view.setTranslationY(data);
                translationY(view, data);
            }
        });

        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
            }
            @Override
            public void onAnimationEnd(Animator animator) {
                final ValueAnimator animatorBack = ValueAnimator.ofFloat(mData, 0);
                animatorBack.setDuration(duration);
                //animator.setDuration(Math.abs(10000));
                animatorBack.setInterpolator(new DecelerateInterpolator());//AccelerateInterpolator  LinearInterpolator
                animatorBack.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float data = (float) animatorBack.getAnimatedValue();
                        //view.setTranslationY(data);
                        translationY(view, data);
                    }
                });

                animatorBack.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animator) {
                    }
                    @Override
                    public void onAnimationEnd(Animator animator) {
                        mCurrentStatus = STATUS_NORMAL;
                    }
                    @Override
                    public void onAnimationCancel(Animator animator) {
                    }
                    @Override
                    public void onAnimationRepeat(Animator animator) {
                    }
                });

                animatorBack.start();
            }
            @Override
            public void onAnimationCancel(Animator animator) {
            }
            @Override
            public void onAnimationRepeat(Animator animator) {
            }
        });

        animator.start();
    }

    private void translationY(View view, float data){
        view.setTranslationY(data);
    }

    /**
     * 拖拉完毕后接口
     */
    public interface OnRefreshListener{
        /** 上拉回调 view:当前拖动的控件  length:控件移动距离*/
        public void dragFinish(View view, float length);
        /** 下拉回调 */
        public void pullFinish(View view, float length);
    }

    public void setOnRefreshListener(OnRefreshListener listener){
        this.mOnRefreshListener = listener;
    }
}